/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "dyn_services_core.h"

#include <limits.h>

#include <securec.h>

#include "logger.h"

#include "dyn_services_core_inner.h"

#define DYN_SID_SHIFT_WIDTH 16U

uint32_t GetRequestServiceId(tee_service_ipc_msg *msg)
{
    return (GetRequestCommand(msg) & 0xFFFF0000) >> DYN_SID_SHIFT_WIDTH;
}

uint32_t GetRequestCommand(tee_service_ipc_msg *msg)
{
    if (msg == NULL) {
        return UINT_MAX;
    }
    return (uint32_t)msg->args_data.arg0;
}

ResultCode CheckClientPermission(uint32_t sndr, const TEE_UUID *uuidList, uint32_t listSize)
{
    if (uuidList == NULL || listSize == 0) {
        LOG_ERROR("CheckClientPermission failed, input is invalid");
        return INVALID_PERM;
    }

    TEE_UUID uuid = {0};
    if (tee_srv_get_uuid_by_sender(sndr, &uuid) != TEE_SUCCESS) {
        LOG_ERROR("CheckClientPermission get uuid failed!");
        return INVALID_PERM;
    }

    for (uint32_t loop = 0; loop < listSize; loop++) {
        const TEE_UUID *curr = uuidList + loop;
        if (memcmp(&uuid, curr, sizeof(TEE_UUID)) == 0) {
            return SUCCESS;
        }
    }

    LOG_ERROR("CheckClientPermission failed for uuid 0x%x", uuid.timeLow);
    return INVALID_PERM;
}

uint8_t *RetainRequestBuffer(tee_service_ipc_msg *msg, uint32_t sndr, uint32_t *buffSize, uint32_t *buffMaxSize)
{
    if (msg == NULL) {
        LOG_ERROR("null msg");
        return NULL;
    }

    uint32_t dataMaxSize = (uint32_t)msg->args_data.arg1;
    uint32_t dataSize = (uint32_t)msg->args_data.arg2;
    uint32_t dataAddr = (uint32_t)msg->args_data.arg3;

    if (dataSize > dataMaxSize || dataMaxSize > MAX_BUFFER_LENGTH) {
        LOG_ERROR("RetainRequestBuffer failed, dataSize = %u, dataMaxSize = %u", dataSize, dataMaxSize);
        return NULL;
    }

    if (buffSize == NULL || buffMaxSize == NULL) {
        LOG_ERROR("RetainRequestBuffer failed, buffSize or buffMaxSize is null");
        return NULL;
    }

    uint32_t vaddr = 0;
    int ret = tee_srv_map_from_task(sndr, dataAddr, dataMaxSize, &vaddr);
    if (ret != 0 || vaddr == 0) {
        LOG_ERROR("tee_srv_map_from_task failed,  ret is %u", (uint32_t)ret);
        return NULL;
    }

    *buffSize = dataSize;
    *buffMaxSize = dataMaxSize;

    return (uint8_t *)(uintptr_t)vaddr;
}

void ReleaseRequestBuffer(uint8_t *buffer, uint32_t bufferMaxSize)
{
    if (buffer == NULL) {
        LOG_ERROR("ReleaseRequestBuffer failed, input is null");
        return;
    }
    tee_srv_unmap_from_task((uint32_t)(uintptr_t)buffer, bufferMaxSize);
}

void ServiceProcessWithCommand(tee_service_ipc_msg *msg, uint32_t sndr, tee_service_ipc_msg_rsp *rsp)
{
    if (rsp == NULL) {
        LOG_ERROR("failed, rsp is null");
        return;
    }

    rsp->ret = TEE_FAIL;

    if (msg == NULL) {
        LOG_ERROR("failed, msg is null");
        return;
    }

    uint32_t sid = GetRequestServiceId(msg);
    uint32_t cmd = GetRequestCommand(msg);

    uint32_t bufferSize = 0;
    uint32_t bufferMaxSize = 0;
    uint8_t *buffer = RetainRequestBuffer(msg, sndr, &bufferSize, &bufferMaxSize);
    if (buffer == NULL) {
        LOG_ERROR("RetainRequestBuffer failed, output is null, sid = 0x%x, request cmd = 0x%x", sid, cmd);
        return;
    }

    SharedDataBuffer sharedData = {.data = buffer, .dataSize = bufferSize, .dataMaxSize = bufferMaxSize};

    rsp->ret = ServiceInvokeCommand(sndr, sid, cmd, &sharedData);
    rsp->msg.args_data.arg4 = (uint64_t)sharedData.dataSize;

    ReleaseRequestBuffer(buffer, bufferMaxSize);
}